package cn.ppool.xmlmapper.builder.parse.model.base;

import cn.ppool.xmlmapper.builder.MapperBuilder;
import cn.ppool.xmlmapper.builder.parse.parsetest.BitType;
import cn.ppool.xmlmapper.builder.parse.parsetest.ConditionModel;
import cn.ppool.xmlmapper.builder.parse.parsetest.TYPE_ENUM;
import cn.ppool.xmlmapper.builder.parse.parsetest.TestStringRunTime;
import cn.ppool.xmlmapper.builder.parse.model.func.IfBO;
import cn.ppool.xmlmapper.builder.xml.XNode;
import cn.ppool.xmlmapper.exctption.*;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;

import javax.script.ScriptEngine;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Log4j2
public abstract class BaseTest extends BaseFuncBO {
    // 常量定义
    public final static String ELSE_IF = "else-if";
    public final static String IF_NULL = "if-null";
    public final static String IF_EMPTY = "if-empty";
    public final static String ELSE = "else";
    public final static String IF_NOT_NULL = "if-not-null";
    public final static String IF_NOT_EMPTY = "if-not-empty";
    public final static String ELSE_IF_NULL = "else-if-null";
    public final static String ELSE_IF_EMPTY = "else-if-empty";
    public final static String ELSE_IF_NOT_NULL = "else-if-not-null";
    public final static String ELSE_IF_NOT_EMPTY = "else-if-not-empty";
    //
    public final static List<String> FUN_ENUM = Arrays.asList(ELSE_IF, IF_NULL, IF_NOT_NULL, ELSE_IF_NULL, ELSE_IF_NOT_NULL, ELSE, IF_EMPTY,
            IF_NOT_EMPTY, ELSE_IF_EMPTY, ELSE_IF_NOT_EMPTY);
    public final static List<String> FUN_ELSE_ENUM = Arrays.asList(ELSE_IF, ELSE_IF_NULL, ELSE_IF_NOT_NULL, ELSE, ELSE_IF_EMPTY, ELSE_IF_NOT_EMPTY);
    public final static List<String> NeedOnlyVariable = Arrays.asList(IF_NULL, IF_EMPTY, IF_NOT_NULL, IF_NOT_EMPTY, ELSE_IF_NULL, ELSE_IF_EMPTY, ELSE_IF_NOT_NULL, ELSE_IF_NOT_EMPTY);
    //
    @Getter
    private String test;
    @Getter
    private String fun;
    @Getter
    private boolean testResult;


    public BaseTest(MapperBuilder configure, XNode node, XNode parentNode) {
        super(configure, node, parentNode);
        this.fun = this.currentNode.getStringAttribute("fun");
        if (this.fun != null && !FUN_ENUM.contains(this.fun)) throw new TestFunTypeNotDefineException("不正确的fun：" + this.fun);
        String test = this.currentNode.getStringAttribute("test");
        if (ELSE.equals(this.fun)) {
            if (test != null) throw new ElseTestNotNullException("else test 不能有表达式");
        } else {
            if (test == null) throw new TestNotDefineException("请定义test");
            this.test = parseTest(test);
        }
    }


    /**
     * 运行test
     *
     * @param var 变量
     */
    protected boolean runTest(Map<String, Object> var) {
        String test = getTest();
        if (test != null && !"".equals(test)) {
            ScriptEngine engine = engineManager.getEngineByName("JavaScript");

            for (Map.Entry<String, Object> val : var.entrySet()) {
                engine.put(val.getKey(), val.getValue());
            }
            try {
                test = super.parseVariable(test, var, null);
                log.trace(test);
                Object testResult = engine.eval(test);
                if (!(testResult instanceof Boolean)) {
                    throw new RuntimeException("表达式只允许返回布尔型类型：" + testResult.getClass().getName() + "，test：" + test);
                }
                this.testResult = (Boolean) testResult;
            } catch (Exception e) {
                log.trace(e);

                if (fun != null && (fun.equals(IfBO.IF_NULL) || fun.equals(IfBO.ELSE_IF_NULL) || fun.equals(IfBO.IF_EMPTY) || fun.equals(IfBO.ELSE_IF_EMPTY)) &&
                        e.getMessage().contains("ReferenceError") && e.getMessage().contains("is not defined")) {
                    this.testResult = true;
                } else {
                    this.testResult = false;
                }
            }
        }
        return testResult;
    }

    /**
     * 解析test条件，总入口
     */
    public String parseTest(String test) {
        TestStringRunTime runTime = new TestStringRunTime(test);
        ConditionModel model = parseTest(runTime);
        if (runTime.getBrackets() != 0) {
            throw new RuntimeException("括号未结束");
        }
        return appendTest(model).toString();
    }


    public ConditionModel parseTest(TestStringRunTime runTime) {
        ConditionModel model = new ConditionModel();
        //
        int wordCount = 0;
        ConditionModel preModal = null;
        TestStringRunTime.TestWordVO preVO = null;
        //
        while (!runTime.isEndIndex()) {
            if (wordCount > 0 && NeedOnlyVariable.contains(this.fun)) {
                throw new TestNeedOnlyVariableException(this.fun + "不允许有多个变量");
            }
            //
            TestStringRunTime.TestWordVO leftWord = runTime.getNextWord();
            if (leftWord.getType() == TYPE_ENUM.brackets && leftWord.getWord().equals("(")) {
                model.writeModel(parseTest(runTime));
                leftWord = runTime.getNextWord();
            } else if (leftWord.getType() == TYPE_ENUM.brackets && leftWord.getWord().equals(")")) {
                break;
            }
            if (leftWord.getType() == null) break;
            //
            if (NeedOnlyVariable.contains(this.getFun())) {
                if (this.getFun().equals(IF_NULL) || this.getFun().equals(ELSE_IF_NULL)) {
                    model.writeWord(leftWord)
                            .writeOperate("==")
                            .writeWord("null", TYPE_ENUM.word);
                } else if (this.fun.equals(IF_NOT_NULL) || this.fun.equals(ELSE_IF_NOT_NULL)) {
                    model.writeWord(leftWord)
                            .writeOperate("!=")
                            .writeWord("null", TYPE_ENUM.word);
                } else if (this.fun.equals(IF_EMPTY) || this.fun.equals(ELSE_IF_EMPTY)) {
                    ConditionModel ifModel = new ConditionModel()
                            .setIsBrackets()
                            .writeWord(leftWord)
                            .writeOperate("==")
                            .writeWord("null", TYPE_ENUM.word)
                            .writeOperate("||")
                            .writeWord(leftWord)
                            .writeOperate("==")
                            .writeWord("''", TYPE_ENUM.word);
                    model.writeModel(ifModel);
                } else if (this.fun.equals(IF_NOT_EMPTY) || this.fun.equals(ELSE_IF_NOT_EMPTY)) {
                    ConditionModel ifModel = new ConditionModel()
                            .setIsBrackets()
                            .writeWord(leftWord)
                            .writeOperate("!=")
                            .writeWord("null", TYPE_ENUM.word)
                            .writeOperate("&&")
                            .writeWord(leftWord)
                            .writeOperate("!=")
                            .writeWord("''", TYPE_ENUM.word);
                    model.writeModel(ifModel);
                }
            } else {
                TestStringRunTime.TestWordVO operateWord = null;
                TestStringRunTime.TestWordVO rightWord = null;
                if (leftWord.getType() == TYPE_ENUM.string || leftWord.getType() == TYPE_ENUM.word) {
                    operateWord = runTime.getNextWord();
                    if (operateWord.getType() != TYPE_ENUM.operate) throw new CharSymbolNotDefineException("无效运算符：" + operateWord.getWord());
                } else if (leftWord.getType() == TYPE_ENUM.operate) {
                    model.writeOperate(leftWord.getWord());
                    preVO = null;
                    preModal = null;
                    continue;
                } else {
                    throw new CharSymbolNotDefineException("无效：" + leftWord.getWord());
                }
                rightWord = runTime.getNextWord();
                //
                if (rightWord.getType() == TYPE_ENUM.autoFillOperator && rightWord.getWord().equals("!empty")) {
                    ConditionModel ifModel = new ConditionModel()
                            .setIsBrackets();
                    wine(leftWord, preVO, preModal, ifModel);
                    ifModel.writeOperate("!=")
                            .writeWord("null", TYPE_ENUM.word)
                            .writeOperate("&&");
                    wine(leftWord, preVO, preModal, ifModel);
                    ifModel.writeOperate("!=")
                            .writeWord("''", TYPE_ENUM.word);
                    model.writeModel(ifModel);
                    //
                    preModal = model;
                    preVO = null;
                } else {
                    wine(leftWord, preVO, preModal, model);
                    model.writeOperate(operateWord.getWord())
                            .writeWord(rightWord);
                    //
                    preVO = rightWord;
                    preModal = null;
                }
            }
            wordCount++;
        }
        return model;
    }

    private void wine(TestStringRunTime.TestWordVO leftWord, TestStringRunTime.TestWordVO preVO, ConditionModel preModal, ConditionModel model) {
        if (leftWord != null) {
            model.write(leftWord);
        }
    }


    /**
     * 将结果转成最终test
     */
    private StringBuilder appendTest(ConditionModel model) {
        StringBuilder sql = new StringBuilder();
        if (model.getBrackets()) sql.append("(");
        for (BitType bit : model.getMathList()) {
            if (bit.getType() == TYPE_ENUM.model) {
                sql.append(appendTest(bit.getModel()));
            } else if (bit.getType() == TYPE_ENUM.word) {
                sql.append(bit.getWord());
            } else if (bit.getType() == TYPE_ENUM.operate) {
                sql.append(bit.getOperate());
            } else if (bit.getType() == TYPE_ENUM.string) {
                sql.append(bit.getWord());
            }
        }
        if (model.getBrackets()) sql.append(")");
        return sql;
    }


}
